package org.jvnc.rfb.state;

/**
* <br><br><center><table border="1" width="80%"><hr>
* <p>
* Copyright (C) 2006-2007 by Deon Wu
* <p>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.0
* of the License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU Lesser General Public License</a>
* for more details.
* <p>
* You should have received a copy of the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">
* GNU Lesser General Public License</a> along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* <hr></table></center>
* **/

import java.io.DataInput;
import java.io.IOException;

import org.jvnc.logging.VLog;
import org.jvnc.rfb.RFBSocketClient;
import org.jvnc.rfb.event.ErrorEvent;
import org.jvnc.rfb.event.RfbEvent;
import org.jvnc.rfb.proto.AuthenicationFragment;
import org.jvnc.rfb.proto.RfbError;

import gnu.rfb.rfb;

public class AuthenicationProtocol extends AbstractStateHandle {
	private static final VLog log = VLog.getLog();
	
	//刚进入安全检查。
	private final static int INIT = 0;
	
	//响应认证信息。
	private final static int RES_AUTH = 1;

	//根据安全协议类型准备认证内容。
	private final static int PRE_AUTH = 3;
	
	//已发送密码，等待服务器端的验证结果。
	private final static int CHECK_AUTH = 2;
	
	//错误 a.上一个阶级版本协商不一协。 b. 密码验证失败。
	private final static int ERROR = -1;
	
	//进入一个阶段。初始化消息。
	private final static int NEXT = 9;
	private int state = 0;
	private AuthenicationFragment authFrag = null;
	private byte secType = 0;
	
	public RfbEvent process() throws IOException {
		RfbEvent event = null;
		switch(state){
			case INIT:
				event = initProcess();
				break;
			case PRE_AUTH:
				event = prepareAuthenication();
				break;
			case RES_AUTH:
				event = authenicationResponse();
				break;
			case CHECK_AUTH:
				event = checkAuth();
				break;
			case NEXT:
				socket.changeState(new InitClient());
				break;
			default:
				socket.changeState(new CloseConnection());
		}

	    return event;
	}
	
	/**
	 * 检查密码验证结果。
	 * @param socket
	 * @return
	 * @throws IOException
	 */
	private RfbEvent checkAuth() throws IOException {
		RfbEvent event = null;
		
		log.debug("Check the SecurityResult message!");
		int re = socket.getInputStream().readInt();
		if(re == rfb.VncAuthOK){			//密码验证成功。
			state = NEXT;
		}else if(re == rfb.VncAuthFailed) {	//密码验证失败。
			// @todo 版本3.8以上的协议会说明出错的原因。
	    	RfbError err = new RfbError();
	    	err.readData(socket.getInputStream());
	    	event = new ErrorEvent(socket,err.getError());
			state = ERROR;
		}
		
		return event;
	}

	
	/**
	 * 根据协议将密码发回服务端。
	 * @param socket
	 * @return
	 * @throws IOException
	 */
	private RfbEvent authenicationResponse() throws IOException {
		log.debug("The password is:" + socket.getPassword());

		authFrag.setPassword(socket.getPassword());
		authFrag.writeData(socket.getOutputStream());
		
		state = CHECK_AUTH;
		return null;
	}
	
	
	/**
	 * 在协议版本商定一致后进入，安全协议初始阶段。服务器反回支持的安全协议。
	 * 如果上一阶段的协商的版本服务端不支持，将返回错误0。
	 * 说明：
	 * a. 3.8版第一位是服务端所支持的安全类型总数，然后是各种安全类型，由客户端选择合适的认证类型。
	 * b. 3.7和3.3直接发送服务端支持的安全类型（单一类型）。
	 * 
	 * @param socket
	 * @return
	 * @throws IOException
	 */
	private RfbEvent initProcess() throws IOException {
		RfbEvent event = null;
		DataInput in = socket.getInputStream();
		int authScheme = 0;
		byte securityHeader = in.readByte();
		//3.8以上协议版本。
		if(0 == securityHeader ){
	    	RfbError err = new RfbError();
	    	err.readData(in);
	    	event = new ErrorEvent(socket,err.getError());
	    	state = ERROR;
	    	return event;
		}
		
		//从服务器端读取所有支持的安全类型。
		byte[] secTypes = new byte[securityHeader];
		in.readFully(secTypes);
		
		log.debug("Server suported securitys:" + securityHeader);
		for(byte t: secTypes){
			log.debug("security type:" + t);
		}
		
		//最好触发一个Event由上层代码。选择支持的协议类型。
		secType = supportedSecurityType(secTypes);
		//event = new ErrorEvent(socket,err.getError());
		if(secType != -1){
			//确认所支持安全类型。
			socket.getOutputStream().writeByte(secType);
			state = PRE_AUTH;
		}else{
			event = new ErrorEvent(socket,"Not found supported security type!");
		}
	    return event;
	}
	
	/*
	 * 从服务器端支持的安全协议中，选择一个客户端可用的安全认证方式。
	 */
	private byte supportedSecurityType(byte[] types){
		for(byte t: types){
			if(t == rfb.VncAuth || t == rfb.NoAuth ){
				return t;
			}
		}
		return -1;
	}
	
	
	/**
	 * 根据安全协议类型准备认证内容。
	 * NoAuth（无认证）
	 * a. 3.8版中会发一个认证结果信息（成功）后再进入客户端初始化阶段。
	 * b. 3.3与3.7直接进入初始化阶段。
	 * 
	 * @param socket
	 * @return
	 * @throws IOException
	 */
	private RfbEvent prepareAuthenication() throws IOException {
		RfbEvent evet = null;
		
		if(this.secType == rfb.VncAuth){
			authFrag = new AuthenicationFragment();
			authFrag.readData(socket.getInputStream());
			this.state = RES_AUTH;
		}else if (this.secType == rfb.NoAuth){
			this.state = CHECK_AUTH;
		}
		
		return null;
	}

}
